package com.dingmouren.paletteimageview;

import com.dingmouren.paletteimageview.factory.PixelMapFactory;
import com.dingmouren.paletteimageview.listner.OnParseColorListener;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.*;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;

import java.lang.ref.WeakReference;

/**
 * Created by dingmouren on 2017/4/25.
 * email:naildingmouren@gmail.com
 */
public class PaletteImageView extends Component implements Component.DrawTask, Component.EstimateSizeListener {

    private static final String ATTRIBUTE_PALETTE_RADIUS = "piv_palette_radius";

    private static final String ATTRIBUTE_PALETTE_SRC = "piv_palette_src";

    private static final String ATTRIBUTE_PALETTE_PADDING = "piv_palette_padding";

    private static final String ATTRIBUTE_PALETTE_OFFSET_X = "piv_palette_offset_x";

    private static final String ATTRIBUTE_PALETTE_OFFSET_Y = "piv_palette_offset_y";

    private static final String ATTRIBUTE_SHADOW_RADIUS = "piv_shadow_radius";

    private static final int MSG = 0x101;
    private static final int DEFAULT_PADDING = 40;
    private static final int DEFAULT_OFFSET = 20;
    private static final int DEFAULT_SHADOW_RADIUS = 20;
    private int mWidth;
    private int mHeight;
    private int mRadius;
    private int mPadding;
    private int mImgId;
    public int mMainColor = -1;
    private int mOffsetX = DEFAULT_OFFSET;
    private int mOffsetY = DEFAULT_OFFSET;
    private int mShadowRadius = DEFAULT_SHADOW_RADIUS;
    private Paint mPaintShadow;
    private Paint mPaint;
    private PixelMap pixelMap;
    private Palette mPalette;
    private RectFloat mRectFShadow;
    private PixelMap mRealPixelMap;
    private int mOnMeasureHeightMode = -1;
    public PaletteImageView mInstance;
    private PixelMap mRoundPixelMap;
    private RectFloat mRoundRectF;
    private BlendMode blendMode;
    private OnParseColorListener mListener;
    private EventHandler mHandler;

    public PaletteImageView(Context context, AttrSet attrSet) {
        super(context, attrSet);
        init(attrSet);
    }

    public PaletteImageView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init(attrSet);
    }

    public PaletteImageView(Context context, AttrSet attrSet, int resId) {
        super(context, attrSet, resId);
        init(attrSet);
    }

    private void init(AttrSet attrSet) {
        this.mInstance = this;
        if (attrSet != null) {
            boolean present = attrSet.getAttr(ATTRIBUTE_PALETTE_RADIUS).isPresent();
            mRadius = present ? attrSet.getAttr(ATTRIBUTE_PALETTE_RADIUS).get().getDimensionValue() : 0;
            present = attrSet.getAttr(ATTRIBUTE_PALETTE_SRC).isPresent();
            mImgId = present ? toIntegerValue(attrSet.getAttr(ATTRIBUTE_PALETTE_SRC).get().getStringValue()) : 0;
            present = attrSet.getAttr(ATTRIBUTE_PALETTE_PADDING).isPresent();
            mPadding = present ? attrSet.getAttr(ATTRIBUTE_PALETTE_PADDING).get().getDimensionValue() : DEFAULT_PADDING;
            present = attrSet.getAttr(ATTRIBUTE_PALETTE_OFFSET_X).isPresent();
            mOffsetX = present ? attrSet.getAttr(ATTRIBUTE_PALETTE_OFFSET_X).get().getDimensionValue() : DEFAULT_OFFSET;
            present = attrSet.getAttr(ATTRIBUTE_PALETTE_OFFSET_Y).isPresent();
            mOffsetY = present ? attrSet.getAttr(ATTRIBUTE_PALETTE_OFFSET_Y).get().getDimensionValue() : DEFAULT_OFFSET;
            present = attrSet.getAttr(ATTRIBUTE_SHADOW_RADIUS).isPresent();
            mShadowRadius = present ? attrSet.getAttr(ATTRIBUTE_SHADOW_RADIUS).get().getDimensionValue() : DEFAULT_SHADOW_RADIUS;
        } else {
            mPadding = DEFAULT_PADDING;
            mOffsetX = DEFAULT_OFFSET;
            mOffsetY = DEFAULT_OFFSET;
            mShadowRadius = DEFAULT_SHADOW_RADIUS;
        }
        mPaintShadow = new Paint();
        mPaintShadow.setDither(true);
        mPaintShadow.setAntiAlias(true);
        mPaint = new Paint();
        mPaint.setDither(true);
        mPaint.setAntiAlias(true);
        blendMode = BlendMode.SRC_IN;
        mHandler = new MyHandler(EventRunner.current(), this);
        addDrawTask(this);
        setEstimateSizeListener(this);
    }

    private int toIntegerValue(String res) {
        try {
            if (res == null) return 0;
            int index = res.indexOf(":") + 1;
            String value = res.substring(index);
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            return 0;
        }
    }


    @Override
    public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
        mWidth = EstimateSpec.getSize(widthMeasureSpec) - mPadding * 2;
        mHeight = EstimateSpec.getSize(heightMeasureSpec) - mPadding * 2;
        mOnMeasureHeightMode = EstimateSpec.getMode(heightMeasureSpec);
        if (mOnMeasureHeightMode == EstimateSpec.UNCONSTRAINT) {
            if (pixelMap != null) {
                mHeight = (int) ((mWidth - mPadding * 2) * (pixelMap.getImageInfo().size.height * 1.0f / pixelMap.getImageInfo().size.width)) + mPadding * 2;
            }
            if (mImgId != 0 && mRealPixelMap != null) {
                mHeight = mRealPixelMap.getImageInfo().size.height + mPadding * 2;
            }
        }
        if (pixelMap != null) {
            mHeight = (int) ((mWidth - mPadding * 2) * (pixelMap.getImageInfo().size.height * 1.0f / pixelMap.getImageInfo().size.width)) + mPadding * 2;
        }
        setEstimatedSize(EstimateSpec.getChildSizeWithMode(mWidth, mWidth, EstimateSpec.NOT_EXCEED),
                EstimateSpec.getChildSizeWithMode(mHeight, mHeight, EstimateSpec.NOT_EXCEED));
        mRectFShadow = new RectFloat(mPadding, mPadding, mWidth - mPadding, mHeight - mPadding);
        mRoundRectF = new RectFloat(0, 0, mWidth - mPadding * 2, mHeight - mPadding * 2);
        if (mImgId != 0 || pixelMap != null) zipBitmap(mImgId, pixelMap, mOnMeasureHeightMode);
        return true;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (mRealPixelMap != null) {
            canvas.drawRoundRect(mRectFShadow, mRadius, mRadius, mPaintShadow);
            if (mRoundPixelMap != null)
                canvas.drawPixelMapHolder(new PixelMapHolder(mRoundPixelMap), mPadding, mPadding, new Paint());
        }
    }

    public void setShadowColor(int color) {
        this.mMainColor = color;
        mHandler.sendEvent(MSG);
    }

    public void setPixelMap(PixelMap pixelMap) {
        this.pixelMap = pixelMap;
        zipBitmap(mImgId, pixelMap, mOnMeasureHeightMode);
    }

    public void setPaletteRadius(int radius) {
        this.mRadius = radius;
        mRoundPixelMap = createRoundConnerImage(mRealPixelMap, mRadius);
        invalidate();
    }

    public void setPaletteShadowOffset(int offsetX, int offsetY) {
        this.mOffsetX = Math.min(offsetX, mPadding);
        if (offsetY > mPadding) {
            this.mOffsetX = mPadding;
        } else {
            this.mOffsetY = offsetY;
        }

        mHandler.sendEvent(MSG);
    }

    public void setPaletteShadowRadius(int radius) {
        this.mShadowRadius = radius;
        mHandler.sendEvent(MSG);
    }

    public void setOnParseColorListener(OnParseColorListener listener) {
        this.mListener = listener;
    }

    private void initShadow(PixelMap pixelMap) {
        if (pixelMap != null) {
            Palette.from(pixelMap).generate(paletteAsyncListener);
        }
    }

    private RectFloat checkValue(RectFloat roundRectF, Size size) {
        if (roundRectF == null) {
            return new RectFloat(0, 0, size.width - mPadding * 2, size.height - mPadding * 2);
        }
        return roundRectF;
    }

    private PixelMap createRoundConnerImage(PixelMap source, int radius) {
        if (source == null) return null;
        PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
        options.pixelFormat = PixelFormat.ARGB_8888;
        if (mWidth - mPadding * 2 > 0 && mHeight - mPadding * 2 > 0) {
            options.size = new Size(mWidth - mPadding * 2, mHeight - mPadding * 2);
        } else {
            options.size = new Size(source.getImageInfo().size.width, source.getImageInfo().size.height);
        }
        PixelMap target = PixelMap.create(options);
        Canvas canvas = new Canvas(new Texture(target));
        canvas.drawRoundRect(checkValue(mRoundRectF, options.size), radius, radius, mPaint);
        mPaint.setBlendMode(blendMode);
        canvas.drawPixelMapHolder(new PixelMapHolder(source), 0, 0, mPaint);
        mPaint.setBlendMode(null);
        return target;
    }

    private void zipBitmap(int imgId, PixelMap pixelMap, int heightNode) {
        if (imgId != 0 && pixelMap == null) {
            pixelMap = PixelMapFactory.CreatePixelMap(getContext(), imgId, mWidth, mHeight);
        }

        if (pixelMap != null) {
            mRealPixelMap = PixelMapFactory.CreatePixelMap(pixelMap, mWidth, mHeight);
            if (mRealPixelMap != null) {
                mRoundPixelMap = createRoundConnerImage(mRealPixelMap, mRadius);
                initShadow(mRealPixelMap);
            }
        }

    }

    private final Palette.PaletteAsyncListener paletteAsyncListener = new Palette.PaletteAsyncListener() {
        @Override
        public void onGenerated(Palette palette) {
            if (palette != null) {
                mPalette = palette;
                mMainColor = palette.getDominantSwatch().getRgb();
                mHandler.sendEvent(MSG);
                if (mListener != null) mListener.onComplete(mInstance);
            } else {
                if (mListener != null) mListener.onFail();
            }
        }
    };

    public int[] getVibrantColor() {
        if (mPalette == null || mPalette.getVibrantSwatch() == null) return null;
        int[] arry = new int[3];
        arry[0] = mPalette.getVibrantSwatch().getTitleTextColor();
        arry[1] = mPalette.getVibrantSwatch().getBodyTextColor();
        arry[2] = mPalette.getVibrantSwatch().getRgb();
        return arry;
    }

    public int[] getDarkVibrantColor() {
        if (mPalette == null || mPalette.getDarkVibrantSwatch() == null) return null;
        int[] arry = new int[3];
        arry[0] = mPalette.getDarkVibrantSwatch().getTitleTextColor();
        arry[1] = mPalette.getDarkVibrantSwatch().getBodyTextColor();
        arry[2] = mPalette.getDarkVibrantSwatch().getRgb();
        return arry;
    }

    public int[] getLightVibrantColor() {
        if (mPalette == null || mPalette.getLightVibrantSwatch() == null) return null;
        int[] arry = new int[3];
        arry[0] = mPalette.getLightVibrantSwatch().getTitleTextColor();
        arry[1] = mPalette.getLightVibrantSwatch().getBodyTextColor();
        arry[2] = mPalette.getLightVibrantSwatch().getRgb();
        return arry;
    }

    public int[] getMutedColor() {
        if (mPalette == null || mPalette.getMutedSwatch() == null) return null;
        int[] arry = new int[3];
        arry[0] = mPalette.getMutedSwatch().getTitleTextColor();
        arry[1] = mPalette.getMutedSwatch().getBodyTextColor();
        arry[2] = mPalette.getMutedSwatch().getRgb();
        return arry;
    }

    public int[] getDarkMutedColor() {
        if (mPalette == null || mPalette.getDarkMutedSwatch() == null) return null;
        int[] arry = new int[3];
        arry[0] = mPalette.getDarkMutedSwatch().getTitleTextColor();
        arry[1] = mPalette.getDarkMutedSwatch().getBodyTextColor();
        arry[2] = mPalette.getDarkMutedSwatch().getRgb();
        return arry;
    }

    public int[] getLightMutedColor() {
        if (mPalette == null || mPalette.getLightMutedSwatch() == null) return null;
        int[] arry = new int[3];
        arry[0] = mPalette.getLightMutedSwatch().getTitleTextColor();
        arry[1] = mPalette.getLightMutedSwatch().getBodyTextColor();
        arry[2] = mPalette.getLightMutedSwatch().getRgb();
        return arry;
    }

    private static class MyHandler extends EventHandler {

        private final WeakReference<PaletteImageView> mPaletteImageViewWeakReference;

        public MyHandler(EventRunner eventRunner, PaletteImageView paletteImageView) {
            super(eventRunner);
            mPaletteImageViewWeakReference = new WeakReference<>(paletteImageView);
        }

        @Override
        public void processEvent(InnerEvent event) {
            if (mPaletteImageViewWeakReference.get() != null) {
                PaletteImageView paletteImageView = mPaletteImageViewWeakReference.get();
                if (paletteImageView.mOffsetX < DEFAULT_OFFSET) paletteImageView.mOffsetX = DEFAULT_OFFSET;
                if (paletteImageView.mOffsetY < DEFAULT_OFFSET) paletteImageView.mOffsetY = DEFAULT_OFFSET;
                if (paletteImageView.mShadowRadius < DEFAULT_SHADOW_RADIUS)
                    paletteImageView.mShadowRadius = DEFAULT_SHADOW_RADIUS;
                BlurDrawLooper blurDrawLooper = new BlurDrawLooper(paletteImageView.mShadowRadius, paletteImageView.mOffsetX, paletteImageView.mOffsetY, new Color(paletteImageView.mMainColor));
                paletteImageView.mPaintShadow.setBlurDrawLooper(blurDrawLooper);
                paletteImageView.invalidate();
            }
        }
    }
}
